<!DOCTYPE html><html lang="zh"><head><meta name="generator" content="Hexo 3.8.0"><meta charset="utf-8"><title>微信开发四篇 - i蝸居年華_谢谢谢_CODE_HOME</title><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><meta name="description" content="[TOC] 序言 上一篇微信相关的开发是《基于WEB微信通信实现智能聊天机器人》. 而怎么保持机器人稳定在线, 不被微信封号, 是一直遗留的问题…"><meta name="keywords" content="微信开发"><meta property="og:type" content="article"><meta property="og:title" content="微信开发四篇"><meta property="og:url" content="https://xiefayang.com/2017/11/02/微信开发四篇/index.html"><meta property="og:site_name" content="i蝸居年華_谢谢谢_CODE_HOME"><meta property="og:description" content="[TOC] 序言 上一篇微信相关的开发是《基于WEB微信通信实现智能聊天机器人》. 而怎么保持机器人稳定在线, 不被微信封号, 是一直遗留的问题…"><meta property="og:locale" content="zh-CN"><meta property="og:image" content="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7.png"><meta property="og:image" content="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E7%94%A8%E6%88%B7%E7%82%B9%E5%87%BB%E6%8E%88%E6%9D%83.png"><meta property="og:image" content="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E4%BF%A1%E6%81%AF.png"><meta property="og:image" content="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E6%94%AF%E4%BB%98%E7%9B%B8%E5%85%B3.png"><meta property="og:image" content="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E6%B7%BB%E5%8A%A0%E6%A8%A1%E7%89%88.png"><meta property="og:image" content="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E6%94%AF%E4%BB%98%E6%95%88%E6%9E%9C.jpg"><meta property="og:updated_time" content="2019-04-29T06:29:29.823Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="微信开发四篇"><meta name="twitter:description" content="[TOC] 序言 上一篇微信相关的开发是《基于WEB微信通信实现智能聊天机器人》. 而怎么保持机器人稳定在线, 不被微信封号, 是一直遗留的问题…"><meta name="twitter:image" content="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7.png"><link rel="icon" href="/images/favicon.svg"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.7.2/css/bulma.css"><link rel="stylesheet" href="https://cdn.bootcss.com/font-awesome/5.4.1/css/all.css"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Ubuntu:400,600|Source+Code+Pro"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@9.12.0/styles/atom-one-dark.css"><style>body>.footer,body>.navbar,body>.section{opacity:0}</style><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lightgallery@1.6.8/dist/css/lightgallery.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/css/justifiedGallery.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/outdatedbrowser@1.1.5/outdatedbrowser/outdatedbrowser.min.css"><link rel="stylesheet" href="/css/back-to-top.css"><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="//hm.baidu.com/hm.js?b59777544dd5d59ce94e191bac62427c";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><link rel="stylesheet" href="/css/progressbar.css"><script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><link rel="stylesheet" href="/css/style.css"></head><body class="is-3-column"><nav class="navbar navbar-main"><div class="container"><div class="navbar-brand is-flex-center"> <a class="navbar-item navbar-logo" href="/"><img src="/images/maotouying.svg" alt="微信开发四篇" height="28"> <span style="font-size:16px;display:inline-block;vertical-align:top;font-weight:600">i蝸居年華_谢谢谢</span></a></div><div class="navbar-menu"><div class="navbar-start"> <a class="navbar-item" href="/">首页</a> <a class="navbar-item" href="/archives">归档</a> <a class="navbar-item" href="/categories">分类</a> <a class="navbar-item" href="/tags">标签</a> <a class="navbar-item" href="/about">关于</a></div><div class="navbar-end"><a class="navbar-item is-hidden-tablet catalogue" title="文章目录" href="javascript:;"><i class="fas fa-list-ul"></i></a><a class="navbar-item search" title="搜索" href="javascript:;"><div class="box01"> <input type="text" id="tip" placeholder="You Know, for Search~"><i class="search_icon"></i></div></a></div></div></div></nav><section class="section"><div class="container"><div class="columns"><div class="column is-8-tablet is-8-desktop is-9-widescreen has-order-2 column-main"><div class="card"><div class="card-content article"><div class="level article-meta is-size-7 is-uppercase is-mobile is-overflow-x-auto"><div class="level-left"> <span style="color:#7a7a7a!important">写于2017-11-02 12:39:04&nbsp;&nbsp;&nbsp;</span><div class="level-item"> <a class="has-link-grey -link" href="/categories/微信开发/">微信开发</a></div> <span class="level-item has-text-grey">30 分钟 读完 (大约 4503 个字)</span><span class="level-item has-text-grey" id="busuanzi_container_page_pv"><i class="far fa-eye"></i> <span id="busuanzi_value_page_pv">0</span>次访问</span></div></div><h1 class="title is-size-3 is-size-4-mobile has-text-weight-normal"> 微信开发四篇</h1><div class="content"><p>[TOC]</p><h3 id="序言"><a href="#序言" class="headerlink" title="序言"></a>序言</h3><blockquote><p>上一篇微信相关的开发是<a href="http://xiefayang.com/2017/04/10/%E5%9F%BA%E4%BA%8EWEB%E5%BE%AE%E4%BF%A1%E9%80%9A%E4%BF%A1%E5%AE%9E%E7%8E%B0%E6%99%BA%E8%83%BD%E8%81%8A%E5%A4%A9%E6%9C%BA%E5%99%A8%E4%BA%BA/"><strong>《基于WEB微信通信实现智能聊天机器人》</strong></a>.</p><p>而怎么保持机器人稳定在线, 不被微信封号, 是一直遗留的问题…<a id="more"></a></p><p>网上一个稳定版本的IOS微信协议已经卖到2000! 所以无力研究了.</p><p>相比之下, 微信公众号开发和小程序更加火爆.</p></blockquote><p>本篇将通过<strong>微信公众平台</strong>, <strong>微信开放平台</strong>, <strong>微信商户平台</strong>. 分为四篇来完成以下几个常用功能.</p><p><strong>第一篇: 微信网页授权</strong></p><p><strong>第二篇: 微信支付</strong></p><p><strong>第三篇: 网站应用微信登录</strong></p><p><strong>第四篇: 推送微信模版消息</strong></p><p><strong>相关文档</strong> (必须了解)</p><ul><li>微信公众平台技术文档: <a href="https://mp.weixin.qq.com/wiki" target="_blank" rel="noopener">https://mp.weixin.qq.com/wiki</a></li><li>微信支付开发文档(公众号支付): <a href="https://pay.weixin.qq.com/wiki/doc/api/index.html" target="_blank" rel="noopener">https://pay.weixin.qq.com/wiki/doc/api/index.html</a></li><li>微信开放平台: <a href="https://open.weixin.qq.com/" target="_blank" rel="noopener">https://open.weixin.qq.com/</a></li></ul><p><strong>使用SDK</strong> (开发效率事半功倍)</p><blockquote><ul><li>微信支付、小程序、公众号&amp;企业号开发Java SDK: <a href="https://github.com/Wechat-Group/weixin-java-tools" target="_blank" rel="noopener">https://github.com/Wechat-Group/weixin-java-tools</a></li><li>可能是目前最好的支付SDK: <a href="https://github.com/Pay-Group/best-pay-sdk" target="_blank" rel="noopener">https://github.com/Pay-Group/best-pay-sdk</a></li></ul></blockquote><h3 id="你需要知道的事"><a href="#你需要知道的事" class="headerlink" title="你需要知道的事"></a>你需要知道的事</h3><p>通过阅读微信公众平台文档: 可以知道公众平台账号分为:</p><ul><li>订阅号</li><li>服务号</li><li>企业号</li></ul><p>每类账号的适用人群不同, 开放的接口权限不同, 申请门槛也不同.</p><p><strong>公众号接口权限说明</strong></p><blockquote><p>官方参考: <a href="http://kf.qq.com/faq/170104AJ3y26170104Yj673y.html" target="_blank" rel="noopener">http://kf.qq.com/faq/170104AJ3y26170104Yj673y.html</a></p></blockquote><p>可以看到, 微信授权和支付功能这两项权限只有<strong>微信认证服务号</strong>才有.</p><p>服务号一般人没有企业资质很难申请到, 更别说微信认证了.</p><p>好的一点是微信公众平台提供了一个测试号, 可以通过手机扫描二维码来获得测试号.</p><blockquote><p>申请地址: <a href="https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login" target="_blank" rel="noopener">https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login</a></p></blockquote><p>注意: 测试公众号有很多体验接口, 包括网页授权获取(无支付权限).</p><h3 id="你需要准备"><a href="#你需要准备" class="headerlink" title="你需要准备"></a>你需要准备</h3><blockquote><p>微信开发需要手机电脑来回调试, 过程还是比较繁琐. 如何在本地方便的进行调试, 就要依赖一些东西.</p></blockquote><ol><li><p>域名<br>微信网页授权的回调地址不能是ip地址. 这一点在官方文档中有提到. 所以在回调到我们的应用程序接口时要支持<br><code>域名/接口路径</code> 的方式, 例如: <code>http://thank.mynatapp.cc/wechat/xxx</code></p></li><li><p>内网映射<br>在手机上调试微信客户端时, 调用PC接口, 肯定无法用 <code>http://localhost:8888/wechat/xxx</code> 的方式了, 为了在本地方便的调试, 就需要用到内网穿透工具. 可以参考我的另一篇: <a href="http://xiefayang.com/2017/11/02/%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F%E5%B7%A5%E5%85%B7/">内网穿透介绍</a></p></li><li><p>抓包工具<br>在手机上点击时, 无法像电脑浏览器中的F12一样看到详细的网络请求.</p><p>所以使用fiddler或charles等抓包工具来抓取网络请求.</p><p>手机Wi-Fi中的HTTP代理中填写主机的ip地址和8888端口(默认)即可.</p></li><li><p>微信web开发者工具</p><p>这是微信官方提供给开发者的工具, 很方便.</p><p>不用下载抓包工具, 也不用设置手机代理.</p><p>可以像电脑浏览器一样模拟手机发送请求. 支持公众号网页调试和小程序调试.</p><p><img src="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7.png" alt="微信开发-开发者工具"></p><p>注意: 微信支付相关的调试在授权时需要在微信公众平台的账号中绑定开发者.</p></li></ol><h3 id="第一篇-微信网页授权"><a href="#第一篇-微信网页授权" class="headerlink" title="第一篇: 微信网页授权"></a>第一篇: 微信网页授权</h3><p>网页授权是指用户在微信客户端访问第三方网页时, 公众号可通过授权机制来获取用户的基本信息. 从而实现业务逻辑.</p><blockquote><p>授权流程: <a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&amp;id=mp1421140842" target="_blank" rel="noopener">https://mp.weixin.qq.com/wiki?t=resource/res_main&amp;id=mp1421140842</a></p></blockquote><p>通过了解授权流程, 可以看到. 网页授权流程主要分为四步：</p><ol><li>引导用户进入授权页面同意授权，获取code</li><li>通过code换取网页授权access_token（与基础支持中的access_token不同）</li><li>如果需要，开发者可以刷新网页授权access_token, 避免过期</li><li>通过网页授权access_token和openid获取用户基本信息（支持UnionID机制）</li></ol><p>主要来看前2步:</p><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># 替换相关参数,相关参数说明在文档中有详细介绍...</span><br><span class="line">https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&amp;redirect_uri=REDIRECT_URI&amp;response_type=code&amp;scope=SCOPE&amp;state=STATE#wechat_redirect</span><br><span class="line"></span><br><span class="line"># 例如: </span><br><span class="line">https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxdcb894a6a375ed25&amp;redirect_uri=http://thank.mynatapp.cc/sell/weixin/auth&amp;response_type=code&amp;scope=snsapi_base&amp;state=STATE#wechat_redirect</span><br></pre></td></tr></table></figure><p>在微信客户端访问该url后, 会进行跳转, <code>redirect_uri</code> 会重定向到我们的应用.</p><p>在跳转之前, 需要用户同意授权. 而用户同意授权的方式, 取决于上面的 <code>scope</code> 参数:</p><ul><li><p>snsapi_base: 静默授权, 微信端用户会在无感知的情况下同意授权, 但是拿到的信息也较少, 只有用户openid.</p></li><li><p>snsapi_userinfo: 微信端会弹出授权页面点击确认. 这种情况下拿到的openid可以再继续拿到用户的更多信息</p><p>如昵称, 性别, 所在地等.</p></li></ul><p>当微信客户端用户同意授权后, 页面将跳转路径为: <code>redirect_uri/?code=CODE&amp;state=STATE</code></p><p>也就是携带着code参数进入我们的应用接口中, 从而在应用接口中获取到code.</p><blockquote><p>code作为换取access_token的票据，每次用户授权带上的code将不一样，code只能使用一次，5分钟未被使用自动过期。</p></blockquote><h5 id="编码实现"><a href="#编码实现" class="headerlink" title="编码实现"></a>编码实现</h5><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">@GetMapping(&quot;/auth&quot;)</span><br><span class="line">public void auth(@RequestParam(&quot;code&quot;) String code)&#123;</span><br><span class="line"></span><br><span class="line">    //第一步: 用户同意授权, 通过回调, 进入该方法, 获取到code</span><br><span class="line">    log.info(&quot;-----进入auth方法: code=&#123;&#125;&quot;, code);</span><br><span class="line"></span><br><span class="line">    //第二步：通过code换取网页授权access_token</span><br><span class="line">    String getTokenUrl = &quot;https://api.weixin.qq.com/sns/oauth2/access_token?appid=&quot;</span><br><span class="line">            +appid+&quot;&amp;secret=&quot;</span><br><span class="line">            +appsecret+&quot;&amp;code=&quot;</span><br><span class="line">            +code+&quot;&amp;grant_type=authorization_code&quot;;</span><br><span class="line">    RestTemplate restTemplate = new RestTemplate();</span><br><span class="line">    String response = restTemplate.getForObject(getTokenUrl, String.class);</span><br><span class="line">    log.info(&quot;-----response=&#123;&#125;&quot;, response );</span><br><span class="line"></span><br><span class="line">    Map&lt;String, Object&gt; map = new Gson().fromJson(response, </span><br><span class="line">            new TypeToken&lt;HashMap&lt;String,Object&gt;&gt;()&#123;&#125;.getType());</span><br><span class="line">    log.info(&quot;--- accessToken=&#123;&#125;&quot;, map.get(&quot;access_token&quot;));</span><br><span class="line">    log.info(&quot;--- openid=&#123;&#125;&quot;, map.get(&quot;openid&quot;));</span><br><span class="line">    log.info(&quot;--- scope=&#123;&#125;&quot;, map.get(&quot;scope&quot;));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>授权过程是可以用手工编写代码来完成, 但比较繁琐. 而github上有很多现成的SDK提供, 所以不需要重复造轮子.</p><h5 id="sdk实现"><a href="#sdk实现" class="headerlink" title="sdk实现"></a>sdk实现</h5><blockquote><p>Github地址: <a href="https://github.com/Wechat-Group/weixin-java-tools" target="_blank" rel="noopener">微信支付、小程序、企业号和公众号（包括服务号和订阅号） Java SDK开发工具包</a></p></blockquote><ol><li><p>Maven依赖</p><figure class="highlight xml hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span></span><br><span class="line">    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.github.binarywang<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span></span><br><span class="line">    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>weixin-java-mp<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.7.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span></span><br><span class="line"><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p>完成微信公众号相关配置读取</p><figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-meta">@Component</span></span><br><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WechatMpConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="hljs-comment">// 配置读取类</span></span><br><span class="line">    <span class="hljs-meta">@Autowired</span></span><br><span class="line">    <span class="hljs-keyword">private</span> WechatAccountConfig wechatAccountConfig;</span><br><span class="line"></span><br><span class="line">    <span class="hljs-meta">@Bean</span></span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> WxMpService <span class="hljs-title">wxMpService</span><span class="hljs-params">()</span></span>&#123;</span><br><span class="line"></span><br><span class="line">        WxMpService wxMpService = <span class="hljs-keyword">new</span> WxMpServiceImpl();</span><br><span class="line">        wxMpService.setWxMpConfigStorage(wxMpConfigStorage());</span><br><span class="line">        <span class="hljs-keyword">return</span> wxMpService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="hljs-meta">@Bean</span></span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> WxMpConfigStorage <span class="hljs-title">wxMpConfigStorage</span><span class="hljs-params">()</span></span>&#123;</span><br><span class="line"></span><br><span class="line">        WxMpInMemoryConfigStorage wxMpConfigStorage = <span class="hljs-keyword">new</span> WxMpInMemoryConfigStorage();</span><br><span class="line">        wxMpConfigStorage.setAppId(wechatAccountConfig.getMpAppId());</span><br><span class="line">        wxMpConfigStorage.setSecret(wechatAccountConfig.getMpAppSecret());</span><br><span class="line">        <span class="hljs-keyword">return</span> wxMpConfigStorage;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>找到该项目的github 开发wiki: 公众号开发 -&gt; OAuth2网页授权</p><p>授权过程分为这几步:</p></li></ol><ul><li><p>首先构造网页授权url，然后构成超链接让用户点击</p><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">wxMpService.oauth2buildAuthorizationUrl(redirectURI, </span><br><span class="line">        WxConsts.OAUTH2_SCOPE_USER_INFO, </span><br><span class="line">        URLEncoder.encode(returnUrl));</span><br></pre></td></tr></table></figure><p>该方法返回一个url, 让微信客户端用户点击, 当用户同意授权, 会回调至设置的redirectURI. 并把code携带过去. 用户点击的页面如下:</p><p><img src="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E7%94%A8%E6%88%B7%E7%82%B9%E5%87%BB%E6%8E%88%E6%9D%83.png" alt="微信开发-用户点击授权"></p><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">WxMpOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);</span><br><span class="line">String openid = wxMpOAuth2AccessToken.getOpenId();</span><br></pre></td></tr></table></figure><p>再通过该方法获取openid. (除了openid以外, 还可以获得用户其它信息), 如下图:</p><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WxMpUser wxMpUser = this.wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null);</span><br></pre></td></tr></table></figure><p><img src="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E4%BF%A1%E6%81%AF.png" alt="微信开发-获取用户信息"></p></li></ul><h3 id="第二篇-微信支付"><a href="#第二篇-微信支付" class="headerlink" title="第二篇: 微信支付"></a>第二篇: 微信支付</h3><blockquote><p>sdk: <a href="https://github.com/Pay-Group/best-pay-sdk" target="_blank" rel="noopener">Pay-Group/best-pay-sdk</a></p><p>虽然微信官方有提供SDK和DEMO, 但是并不好用</p><p>这款SDK是在github中找的, 里面有很详细的视频说明, 而且还提供支付账号借用, 对于拿不到公众号认证资格的开发者来说很方便学习.</p></blockquote><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;!--微信支付相关SDK--&gt;</span><br><span class="line">&lt;dependency&gt;</span><br><span class="line">  	&lt;groupId&gt;cn.springboot&lt;/groupId&gt;</span><br><span class="line">  	&lt;artifactId&gt;best-pay-sdk&lt;/artifactId&gt;</span><br><span class="line">  	&lt;version&gt;1.1.0&lt;/version&gt;</span><br><span class="line">&lt;/dependency&gt;</span><br></pre></td></tr></table></figure><p><img src="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E6%94%AF%E4%BB%98%E7%9B%B8%E5%85%B3.png" alt="微信开发-支付相关"></p><h5 id="支付流程"><a href="#支付流程" class="headerlink" title="支付流程"></a>支付流程</h5><p>下面来描述一个最基本的支付流程, 包括以下几个环节:</p><ul><li>统一下单</li><li>微信客户端H5支付</li><li>接收微信异步通知</li><li>退款</li></ul><h5 id="1-统一下单API"><a href="#1-统一下单API" class="headerlink" title="1. 统一下单API"></a>1. 统一下单API</h5><blockquote><p>应用场景</p></blockquote><p>除被扫支付场景以外，商户系统先调用该接口在微信支付服务后台生成预支付交易单，返回正确的预支付交易回话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付。</p><ol><li><p>配置WxPayH5Config</p><figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-meta">@Component</span></span><br><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WechatPayConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  	<span class="hljs-comment">// 配置读取类</span></span><br><span class="line">    <span class="hljs-meta">@Autowired</span></span><br><span class="line">    <span class="hljs-keyword">private</span> WechatAccountConfig accountConfig;</span><br><span class="line"></span><br><span class="line">    <span class="hljs-meta">@Bean</span></span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> BestPayServiceImpl <span class="hljs-title">bestPayService</span><span class="hljs-params">()</span> </span>&#123;</span><br><span class="line">        BestPayServiceImpl bestPayService = <span class="hljs-keyword">new</span> BestPayServiceImpl();</span><br><span class="line">        bestPayService.setWxPayH5Config(wxPayH5Config());</span><br><span class="line">        <span class="hljs-keyword">return</span> bestPayService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="hljs-meta">@Bean</span></span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> WxPayH5Config <span class="hljs-title">wxPayH5Config</span><span class="hljs-params">()</span> </span>&#123;</span><br><span class="line">        WxPayH5Config wxPayH5Config = <span class="hljs-keyword">new</span> WxPayH5Config();</span><br><span class="line">        wxPayH5Config.setAppId(accountConfig.getMpAppId());</span><br><span class="line">        wxPayH5Config.setAppSecret(accountConfig.getMpAppSecret());</span><br><span class="line">        wxPayH5Config.setMchId(accountConfig.getMchId());</span><br><span class="line">        wxPayH5Config.setMchKey(accountConfig.getMchKey());</span><br><span class="line">        wxPayH5Config.setKeyPath(accountConfig.getKeyPath());</span><br><span class="line">        wxPayH5Config.setNotifyUrl(accountConfig.getNotifyUrl());</span><br><span class="line">        <span class="hljs-keyword">return</span> wxPayH5Config;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>设置发起支付的相关参数 <code>PayRequest</code> , 调用创建支付接口</p><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">PayResponse payResponse = this.bestPayService.pay(payRequest);</span><br></pre></td></tr></table></figure><p>这样就完成了生成预付单. <code>payResponse</code> 返回结果中携带预付单信息(prepay_id及其他信息)</p></li></ol><h5 id="2-微信内H5调起支付"><a href="#2-微信内H5调起支付" class="headerlink" title="2. 微信内H5调起支付"></a>2. 微信内H5调起支付</h5><p>注意: 必须在微信客户端浏览器中</p><p>下单的返回对象 <code>payResponse</code> 会返回 <code>getBrandWCPayRequest</code> 中的参数</p><blockquote><p>参数列表详细描述:<a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&amp;index=6" target="_blank" rel="noopener">https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&amp;index=6</a></p></blockquote><figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-function">function <span class="hljs-title">onBridgeReady</span><span class="hljs-params">()</span></span>&#123;</span><br><span class="line">   WeixinJSBridge.invoke(</span><br><span class="line">       <span class="hljs-string">'getBrandWCPayRequest'</span>, &#123;</span><br><span class="line">           <span class="hljs-string">"appId"</span>:<span class="hljs-string">"wx2421b1c4370ec43b"</span>,     <span class="hljs-comment">//公众号名称，由商户传入     </span></span><br><span class="line">           <span class="hljs-string">"timeStamp"</span>:<span class="hljs-string">"1395712654"</span>,         <span class="hljs-comment">//时间戳，自1970年以来的秒数     </span></span><br><span class="line">           <span class="hljs-string">"nonceStr"</span>:<span class="hljs-string">"e61463f8efa94090b1f366cccfbbb444"</span>, <span class="hljs-comment">//随机串     </span></span><br><span class="line">           <span class="hljs-string">"package"</span>:<span class="hljs-string">"prepay_id=u802345jgfjsdfgsdg888"</span>,     </span><br><span class="line">           <span class="hljs-string">"signType"</span>:<span class="hljs-string">"MD5"</span>,         <span class="hljs-comment">//微信签名方式：     </span></span><br><span class="line">           <span class="hljs-string">"paySign"</span>:<span class="hljs-string">"70EA570631E4BB79628FBCA90534C63FF7FADD89"</span> <span class="hljs-comment">//微信签名 </span></span><br><span class="line">       &#125;,</span><br><span class="line">       function(res)&#123;     </span><br><span class="line">           <span class="hljs-keyword">if</span>(res.err_msg == <span class="hljs-string">"get_brand_wcpay_request:ok"</span> ) &#123;&#125;     <span class="hljs-comment">// 使用以上方式判断前端返回,微信团队郑重提示：res.err_msg将在用户支付成功后返回    ok，但并不保证它绝对可靠。 </span></span><br><span class="line">       &#125;</span><br><span class="line">   ); </span><br><span class="line">&#125;</span><br><span class="line"><span class="hljs-keyword">if</span> (typeof WeixinJSBridge == <span class="hljs-string">"undefined"</span>)&#123;</span><br><span class="line">   <span class="hljs-keyword">if</span>( document.addEventListener )&#123;</span><br><span class="line">       document.addEventListener(<span class="hljs-string">'WeixinJSBridgeReady'</span>, onBridgeReady, <span class="hljs-keyword">false</span>);</span><br><span class="line">   &#125;<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (document.attachEvent)&#123;</span><br><span class="line">       document.attachEvent(<span class="hljs-string">'WeixinJSBridgeReady'</span>, onBridgeReady); </span><br><span class="line">       document.attachEvent(<span class="hljs-string">'onWeixinJSBridgeReady'</span>, onBridgeReady);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;<span class="hljs-keyword">else</span>&#123;</span><br><span class="line">   onBridgeReady();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时在微信客户端生成JSAPI页面, 用户点击即可发起支付.</p><p><strong>关于支付成功的判断</strong></p><p>后台的商户系统通常会根据支付状态去修改商品的状态, 库存等.</p><p>如何判断用户是否支付成功呢?</p><p>在H5调起支付中, 网页支付接口中在完成后会返回 <code>err_msg</code> .</p><table><thead><tr><th>返回值</th><th>描述</th></tr></thead><tbody><tr><td>get_brand_wcpay_request:ok</td><td>支付成功</td></tr><tr><td>get_brand_wcpay_request:cancel</td><td>支付过程中用户取消</td></tr><tr><td>get_brand_wcpay_request:fail</td><td>支付失败</td></tr></tbody></table><p>但是这种方式并不可靠, 微信团队也有说明.</p><p>所以, 可靠的方式是通过微信返回给我们的支付结果通知 .</p><h5 id="3-支付结果通知API"><a href="#3-支付结果通知API" class="headerlink" title="3. 支付结果通知API"></a>3. 支付结果通知API</h5><ul><li><p><strong>微信异步通知</strong></p><blockquote><p>支付完成后，微信会把相关支付结果和用户信息发送给商户，商户需要接收处理，并返回应答。</p><p>注意微信的异步通知需要设置 notify_url , 否则无法接收.</p></blockquote><p>关于 <code>notify_url</code> : 微信没有设置白名单, 所以只要外网能访问到的地址即可.</p><p>例如: <a href="http://thank.mynatapp.cc/wechat/pay/notify" target="_blank" rel="noopener">http://thank.mynatapp.cc/wechat/pay/notify</a></p><p>微信返回给商户系统的数据如下: (SUCCESS情况下)</p><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">&lt;xml&gt;</span><br><span class="line">  &lt;appid&gt;&lt;![CDATA[wx2421b1c4370ec43b]]&gt;&lt;/appid&gt;</span><br><span class="line">  &lt;attach&gt;&lt;![CDATA[支付测试]]&gt;&lt;/attach&gt;</span><br><span class="line">  &lt;bank_type&gt;&lt;![CDATA[CFT]]&gt;&lt;/bank_type&gt;</span><br><span class="line">  &lt;fee_type&gt;&lt;![CDATA[CNY]]&gt;&lt;/fee_type&gt;</span><br><span class="line">  &lt;is_subscribe&gt;&lt;![CDATA[Y]]&gt;&lt;/is_subscribe&gt;</span><br><span class="line">  &lt;mch_id&gt;&lt;![CDATA[10000100]]&gt;&lt;/mch_id&gt;</span><br><span class="line">  &lt;nonce_str&gt;&lt;![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]&gt;&lt;/nonce_str&gt;</span><br><span class="line">  &lt;openid&gt;&lt;![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]&gt;&lt;/openid&gt;</span><br><span class="line">  &lt;out_trade_no&gt;&lt;![CDATA[1409811653]]&gt;&lt;/out_trade_no&gt;</span><br><span class="line">  &lt;result_code&gt;&lt;![CDATA[SUCCESS]]&gt;&lt;/result_code&gt;</span><br><span class="line">  &lt;return_code&gt;&lt;![CDATA[SUCCESS]]&gt;&lt;/return_code&gt;</span><br><span class="line">  &lt;sign&gt;&lt;![CDATA[B552ED6B279343CB493C5DD0D78AB241]]&gt;&lt;/sign&gt;</span><br><span class="line">  &lt;sub_mch_id&gt;&lt;![CDATA[10000100]]&gt;&lt;/sub_mch_id&gt;</span><br><span class="line">  &lt;time_end&gt;&lt;![CDATA[20140903131540]]&gt;&lt;/time_end&gt;</span><br><span class="line">  &lt;total_fee&gt;1&lt;/total_fee&gt;</span><br><span class="line">  &lt;trade_type&gt;&lt;![CDATA[JSAPI]]&gt;&lt;/trade_type&gt;</span><br><span class="line">  &lt;transaction_id&gt;&lt;![CDATA[1004400740201409030005092168]]&gt;&lt;/transaction_id&gt;</span><br><span class="line">&lt;/xml&gt;</span><br></pre></td></tr></table></figure><p>具体的参数信息在官方API中都有详细描述</p><p>接受到微信异步通知后, 通过SDK来接收微信支付结果消息</p><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">// 1. 验证签名 (SDK已做)</span><br><span class="line"></span><br><span class="line">// 2. 验证支付状态 (SDK已做)</span><br><span class="line"></span><br><span class="line">PayResponse payResponse = this.bestPayService.asyncNotify(notifyData);</span><br><span class="line"></span><br><span class="line">// 3. 商户系统自己做验证金额, 支付人等处理.</span><br><span class="line"></span><br><span class="line">// 4. 验证完成后即可修改商品和订单的相关状态</span><br><span class="line"></span><br><span class="line">// 5. 返回微信处理结果</span><br></pre></td></tr></table></figure></li><li><p><strong>商户同步返回处理结果</strong></p><blockquote><p>商户系统在接收到微信的异步通知后, 就可以进行商品状态/库存修改等操作了,</p><p>处理完成后需要告诉微信处理结果, 否则微信会一直发送异步通知过来.</p></blockquote><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;xml&gt;</span><br><span class="line">  &lt;return_code&gt;&lt;![CDATA[SUCCESS]]&gt;&lt;/return_code&gt;</span><br><span class="line">  &lt;return_msg&gt;&lt;![CDATA[OK]]&gt;&lt;/return_msg&gt;</span><br><span class="line">&lt;/xml&gt;</span><br></pre></td></tr></table></figure></li></ul><h5 id="4-申请退款API"><a href="#4-申请退款API" class="headerlink" title="4. 申请退款API"></a>4. 申请退款API</h5><blockquote><p>当交易发生之后一段时间内，由于买家或者卖家的原因需要退款时，卖家可以通过退款接口将支付款退还给买家，微信支付将在收到退款请求并且验证成功之后，按照退款规则将支付款按原路退到买家帐号上。</p></blockquote><p>与统一下单接口很类似, 不同的是申请退款请求需要双向证书, 开发者需要<a href="http://www.xiefayang.com/2017/11/02/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91/pay.weixin.qq.com" target="_blank" rel="noopener">微信商户平台</a>下载相关证书文件.</p><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># 设置RefundRequest</span><br><span class="line">RefundResponse refundResponse = this.bestPayService.refund(refundRequest);</span><br></pre></td></tr></table></figure><h3 id="第三篇-微信登录功能"><a href="#第三篇-微信登录功能" class="headerlink" title="第三篇: 微信登录功能"></a>第三篇: 微信登录功能</h3><p>这里介绍网站应用的微信登录功能, 该功能是在微信开放平台下.</p><p>网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统.</p><p>微信OAuth2.0授权登录是让微信用户以微信身份安全登录第三方应用或网站.</p><blockquote><p>扫码登录体验: <a href="https://passport.yhd.com/wechat/login.do" target="_blank" rel="noopener">一号店</a></p></blockquote><p><strong>需要注意</strong>!</p><ul><li>微信登录相关的授权除了要有开放平台开发者账号, 还需要用户号有企业资质的认证. 普通账号无法授权!</li><li>开放平台的授权流程和公众平台类似, 但是 <code>AppID</code> 和 <code>AppSecret</code> 不同, 所以获得的 <code>openid</code> 也不同.</li><li>每个用户针对每个公众号会产生一个安全的 <code>OpenID</code> . 一个微信号在不同公众号下的 <code>openid</code> 是不同的</li></ul><h5 id="授权流程"><a href="#授权流程" class="headerlink" title="授权流程"></a>授权流程</h5><ol><li>第三方发起微信授权登录请求，微信用户允许授权第三方应用后，微信会拉起应用或重定向到第三方网站，并且带上授权临时票据code参数；</li><li>通过code参数加上AppID和AppSecret等，通过API换取access_token；</li><li>通过access_token进行接口调用，获取用户基本数据资源或帮助用户实现基本操作。</li></ol><p>详细的流程参考<a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&amp;t=resource/res_list&amp;verify=1&amp;id=open1419316505&amp;token=&amp;lang=zh_CN" target="_blank" rel="noopener">开发指南</a>, 这里我使用sdk方式:<a href="https://github.com/Pay-Group/best-pay-sdk" target="_blank" rel="noopener">best-pay-sdk</a></p><h5 id="sdk实现-1"><a href="#sdk实现-1" class="headerlink" title="sdk实现"></a>sdk实现</h5><p>首先完成微信开放平台相关配置, 配置 <code>AppID</code> 和 <code>AppSecret</code></p><figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-meta">@Component</span></span><br><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WechatOpenConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  	<span class="hljs-comment">// 配置读取类</span></span><br><span class="line">    <span class="hljs-meta">@Autowired</span></span><br><span class="line">    <span class="hljs-keyword">private</span> WechatAccountConfig wechatAccountConfig;</span><br><span class="line"></span><br><span class="line">    <span class="hljs-meta">@Bean</span></span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> WxMpService <span class="hljs-title">wxOpenService</span><span class="hljs-params">()</span></span>&#123;</span><br><span class="line">        WxMpService wxOpenService = <span class="hljs-keyword">new</span> WxMpServiceImpl();</span><br><span class="line">        wxOpenService.setWxMpConfigStorage(wxOpenConfigStorage());</span><br><span class="line">        <span class="hljs-keyword">return</span> wxOpenService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="hljs-meta">@Bean</span></span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> WxMpConfigStorage <span class="hljs-title">wxOpenConfigStorage</span><span class="hljs-params">()</span></span>&#123;</span><br><span class="line">        WxMpInMemoryConfigStorage wxMpInMemoryConfigStorage = <span class="hljs-keyword">new</span> WxMpInMemoryConfigStorage();</span><br><span class="line">        wxMpInMemoryConfigStorage.setAppId(wechatAccountConfig.getOpenAppId());</span><br><span class="line">        wxMpInMemoryConfigStorage.setSecret(wechatAccountConfig.getOpenAppSecret());</span><br><span class="line">        <span class="hljs-keyword">return</span> wxMpInMemoryConfigStorage;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>第一步: 请求code</strong></p><p>用户向第三方应用发起登录请求:</p><figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-meta">@GetMapping</span>(<span class="hljs-string">"qrAuthorize"</span>)</span><br><span class="line"><span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">qrAuthorize</span><span class="hljs-params">(@RequestParam(<span class="hljs-string">"state"</span>)</span> String state)</span>&#123;</span><br><span class="line"></span><br><span class="line">	String redirectUrl = <span class="hljs-keyword">this</span>.projectUrlConfig.getWechatOpenAuthorize() </span><br><span class="line">							+ <span class="hljs-string">"/wechat/qrUserInfo"</span>;</span><br><span class="line">	String qrPageUrl = wxOpenService.buildQrConnectUrl(redirectUrl, </span><br><span class="line">							WxConsts.QRCONNECT_SCOPE_SNSAPI_LOGIN, state);</span><br><span class="line">	<span class="hljs-keyword">return</span> <span class="hljs-string">"redirect:"</span> + qrPageUrl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>跳转至微信登录的二维码界面, 等待用户扫码授权后, 会重定向到 <code>redirectUrl</code> , 并携带 <code>code</code> 和 <code>state</code> 参数.</p><p><code>redirect_uri?code=CODE&amp;state=STATE</code></p><p><strong>第二步: 通过code换取access_token</strong></p><figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-meta">@GetMapping</span>(<span class="hljs-string">"qrUserInfo"</span>)</span><br><span class="line"><span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">qrUserInfo</span><span class="hljs-params">(@RequestParam(<span class="hljs-string">"code"</span>)</span> String code, </span></span><br><span class="line"><span class="hljs-function">						 @<span class="hljs-title">RequestParam</span><span class="hljs-params">(<span class="hljs-string">"state"</span>)</span> String state) </span>&#123;</span><br><span class="line"></span><br><span class="line">	WxMpOAuth2AccessToken wxMpOAuth2AccessToken = <span class="hljs-keyword">new</span> WxMpOAuth2AccessToken();</span><br><span class="line">	<span class="hljs-keyword">try</span> &#123;</span><br><span class="line">		wxMpOAuth2AccessToken = <span class="hljs-keyword">this</span>.wxOpenService.oauth2getAccessToken(code);</span><br><span class="line">	&#125; <span class="hljs-keyword">catch</span> (WxErrorException e) &#123;</span><br><span class="line">		log.error(<span class="hljs-string">"[微信网页授权错误: &#123;&#125;]"</span>, e);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	log.info(<span class="hljs-string">"state=&#123;&#125;"</span>, state);</span><br><span class="line">	log.info(<span class="hljs-string">"wxMpOAuth2AccessToken=&#123;&#125;"</span>, wxMpOAuth2AccessToken);</span><br><span class="line">	log.info(<span class="hljs-string">"accessToken=&#123;&#125;"</span>, wxMpOAuth2AccessToken.accessToken);</span><br><span class="line">	log.info(<span class="hljs-string">"openid=&#123;&#125;"</span>, wxMpOAuth2AccessToken.getOpenId());</span><br><span class="line">	<span class="hljs-keyword">return</span> <span class="hljs-string">"redirect:"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>获取到用户的 <code>openid</code> , 授权完成.</p><h3 id="第四篇-微信模版消息推送"><a href="#第四篇-微信模版消息推送" class="headerlink" title="第四篇: 微信模版消息推送"></a>第四篇: 微信模版消息推送</h3><p>这里用到微信公众平台中消息管理功能.</p><blockquote><p>模板消息仅用于公众号向用户发送重要的服务通知，只能用于符合其要求的服务场景中，如信用卡刷卡</p><p>通知，商品购买成功通知等。不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息。</p><p>官方文档: <a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&amp;id=mp1433751277" target="_blank" rel="noopener">https://mp.weixin.qq.com/wiki?t=resource/res_main&amp;id=mp1433751277</a></p></blockquote><p>通过阅读文档: 这种容易产生骚扰, 欺骗功能的接口肯定还是需要公众账号, 认证服务号的.</p><p>还好的一点是测试号有<strong>模版消息(业务通知)</strong>的接口. 所以这里我用测试账号.</p><h5 id="1-设置模版"><a href="#1-设置模版" class="headerlink" title="1. 设置模版"></a>1. 设置模版</h5><p>在公众号管理中添加模版(测试号也类似)</p><p><img src="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E6%B7%BB%E5%8A%A0%E6%A8%A1%E7%89%88.png" alt="微信开发-添加模版"></p><ul><li><p>模版标题: 随意填写</p></li><li><p>模版内容: 如果是公众号可以选择现有的模版, 测试号只能自己输入(需要遵循格式), 例如:</p><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;&#123;first.DATA&#125;&#125;</span><br><span class="line">收款方:&#123;&#123;keyword1.DATA&#125;&#125;</span><br><span class="line">支付方:&#123;&#123;keyword2.DATA&#125;&#125;</span><br><span class="line">支付方式:&#123;&#123;keyword3.DATA&#125;&#125;</span><br><span class="line">交易状态:&#123;&#123;keyword4.DATA&#125;&#125; </span><br><span class="line">&#123;&#123;remark.DATA&#125;&#125;</span><br></pre></td></tr></table></figure></li><li><p>模版ID: 自动生成(用于接口调用)</p></li></ul><h5 id="2-发送模版消息"><a href="#2-发送模版消息" class="headerlink" title="2. 发送模版消息"></a>2. 发送模版消息</h5><figure class="highlight plain hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">http请求方式: POST</span><br><span class="line">https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN</span><br></pre></td></tr></table></figure><p>post发送模版内容data数据</p><blockquote><p>使用<a href="https://github.com/Wechat-Group/weixin-java-tool" target="_blank" rel="noopener">sdk</a> 实现</p></blockquote><figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-meta">@Autowired</span></span><br><span class="line"><span class="hljs-keyword">private</span> WxMpService wxMpService;</span><br><span class="line"></span><br><span class="line"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">pushTemplateMessage</span><span class="hljs-params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	String templateId = <span class="hljs-string">"f-wEkgooC6My0792dHeoEZ-Zd85cOXYJShMB45sCdV4"</span>;</span><br><span class="line">	String openId = <span class="hljs-string">"ol4h7wot1fneGq1RN0LfWaNBtsYQ"</span>;</span><br><span class="line"></span><br><span class="line">	WxMpTemplateMessage templateMessage = <span class="hljs-keyword">new</span> WxMpTemplateMessage();</span><br><span class="line">	templateMessage.setTemplateId(templateId);</span><br><span class="line">	templateMessage.setToUser(openId);</span><br><span class="line"></span><br><span class="line">	List&lt;WxMpTemplateData&gt; data = Arrays.asList(</span><br><span class="line">			<span class="hljs-keyword">new</span> WxMpTemplateData(<span class="hljs-string">"first"</span>, <span class="hljs-string">"微信支付凭证哈哈哈！"</span>),</span><br><span class="line">			<span class="hljs-keyword">new</span> WxMpTemplateData(<span class="hljs-string">"keyword1"</span>, <span class="hljs-string">"谢谢谢"</span>, <span class="hljs-string">"#173177"</span>),</span><br><span class="line">			<span class="hljs-keyword">new</span> WxMpTemplateData(<span class="hljs-string">"keyword2"</span>, <span class="hljs-string">"可口可乐"</span>),</span><br><span class="line">			<span class="hljs-keyword">new</span> WxMpTemplateData(<span class="hljs-string">"keyword3"</span>, <span class="hljs-string">"支付宝转账"</span>),</span><br><span class="line">			<span class="hljs-keyword">new</span> WxMpTemplateData(<span class="hljs-string">"keyword4"</span>, <span class="hljs-string">"$10000 欠款未付"</span>, <span class="hljs-string">"#CD2626"</span>),</span><br><span class="line">			<span class="hljs-keyword">new</span> WxMpTemplateData(<span class="hljs-string">"remark"</span>, <span class="hljs-string">"欢迎再次购买！"</span>)</span><br><span class="line">	);</span><br><span class="line"></span><br><span class="line">	templateMessage.setData(data);</span><br><span class="line"></span><br><span class="line">	<span class="hljs-keyword">try</span> &#123;</span><br><span class="line">		wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);</span><br><span class="line">	&#125; <span class="hljs-keyword">catch</span> (WxErrorException e) &#123;</span><br><span class="line">		log.error(<span class="hljs-string">"微信模版消息发送失败, &#123;&#125;"</span>, e);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>用了SDK, 就非常简单吧! 效果如图:</p><p><img src="https://blog-md-pic-1259135436.cos.ap-chengdu.myqcloud.com/%E5%85%B6%E5%AE%83/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91-%E6%94%AF%E4%BB%98%E6%95%88%E6%9E%9C.jpg" alt="微信开发-支付效果"></p></div><ul class="post-copyright"><li><strong>本文标题：</strong><a href="https://xiefayang.com/2017/11/02/微信开发四篇/">微信开发四篇</a></li><li><strong>本文作者：</strong><a href="https://xiefayang.com">i蝸居年華_谢谢谢</a></li><li><strong>本文链接：</strong><a href="https://xiefayang.com/2017/11/02/微信开发四篇/">https://xiefayang.com/2017/11/02/微信开发四篇/</a></li><li><strong>发布时间：</strong>2017-11-02</li><li><strong>版权声明：</strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" rel="external nofollow" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明出处！</li></ul><div class="level is-size-7 is-uppercase"><div class="level-start"><div class="level-item"> <span class="is-size-6 has-text-grey has-mr-7">#</span> <a class="has-link-grey -link" href="/tags/微信开发/">微信开发</a></div></div></div><div class="social-share"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/social-share.js@1.0.16/dist/css/share.min.css"><script src="https://cdn.jsdelivr.net/npm/social-share.js@1.0.16/dist/js/social-share.min.js"></script></div></div><div class="card"><div class="card-content"><h3 class="menu-label has-text-centered">坚持原创技术分享，您的支持将鼓励我继续创作！</h3><div class="buttons is-centered"><a class="button is-info donate"><span class="icon is-small"><i class="fab fa-alipay"></i></span> <span>支付宝</span><div class="qrcode"><img src="/images/no2.jpg" alt="支付宝"></div></a><a class="button is-success donate"><span class="icon is-small"><i class="fab fa-weixin"></i></span> <span>微信</span><div class="qrcode"><img src="/images/no.gif" alt="微信"></div></a></div></div></div><div class="card card-transparent"><div class="level post-navigation is-flex-wrap is-mobile"><div class="level-start"><a class="level level-item has-link-grey article-nav-prev" href="/2017/11/15/Activiti初探/"><i class="level-item fas fa-chevron-left"></i> <span class="level-item">Activiti初探</span></a></div><div class="level-end"> <a class="level level-item has-link-grey article-nav-next" href="/2017/11/01/内网穿透工具/"><span class="level-item">内网穿透工具</span><i class="level-item fas fa-chevron-right"></i></a></div></div></div><div class="card"><div class="card-content"><h3 class="title is-5 has-text-weight-normal">评论</h3><div id="valine-thread" class="content"></div><script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script><script src="/js/Valine.min.js"></script><script>new Valine({el:"#valine-thread",notify:!1,verify:!1,app_id:"yVPV2fAKC1B90Xnh2JjuPEAb-gzGzoHsz",app_key:"1i9DgF7j75Uq6OuM23etjjE9",placeholder:"Just go go, 支持Markdown~"})</script></div></div></div><div class="column is-4-tablet is-4-desktop is-3-widescreen has-order-1 column-left"><div class="card widget"><div class="card-content"><nav class="level"><div class="level-item has-text-centered"><div> <img class="image is-128x128 has-mb-6" src="/images/avatar.png" alt="i蝸居年華_谢谢谢" style="border-radius:50%"><p class="is-size-4 is-block"> i蝸居年華_谢谢谢</p><p class="is-size-6 is-block"> Just for fun</p><p class="is-size-6 is-flex is-flex-center has-text-grey"><i class="fas fa-map-marker-alt has-mr-7"></i> <span>Beijing, China</span></p></div></div></nav><nav class="level is-mobile"><div class="level-item has-text-centered is-marginless"><div><p class="heading"> 文章</p><p class="title has-text-weight-normal"> 49</p></div></div><div class="level-item has-text-centered is-marginless"><div><p class="heading"> 分类</p><p class="title has-text-weight-normal"> 13</p></div></div><div class="level-item has-text-centered is-marginless"><div><p class="heading"> 标签</p><p class="title has-text-weight-normal"> 32</p></div></div></nav><div class="level"> <a class="level-item button is-link is-rounded" href="https://github.com/thank037" target="_blank">关注我</a></div><div class="level is-mobile"> <a class="level-item button is-white is-marginless" target="_blank" title="github" href="https://github.com/thank037" name><img src="/images/github.svg" style="width:20px;height:20px"></a> <a class="level-item button is-white is-marginless" target="_blank" title="gitee" href="https://gitee.com/thank037" name><img src="/images/gitee.svg" style="width:20px;height:20px"></a> <a class="level-item button is-white is-marginless" target="_blank" title="weibo" href="https://weibo.com/u/2139090054" name><img src="/images/weibo.svg" style="width:20px;height:20px"></a> <a class="level-item button is-white is-marginless" target="_blank" title="email" href="mailto:coderthank@163.com" name><img src="/images/email.svg" style="width:20px;height:20px"></a></div></div></div><div class="card widget column-left is-sticky" id="toc"><div class="card-content"><div class="menu"><h3 class="menu-label"> 文章目录</h3><ul class="menu-list"><li> <a class="is-flex" href="#序言"><span class="has-mr-6">1</span> <span>序言</span></a></li><li> <a class="is-flex" href="#你需要知道的事"><span class="has-mr-6">2</span> <span>你需要知道的事</span></a></li><li> <a class="is-flex" href="#你需要准备"><span class="has-mr-6">3</span> <span>你需要准备</span></a></li><li> <a class="is-flex" href="#第一篇-微信网页授权"><span class="has-mr-6">4</span> <span>第一篇: 微信网页授权</span></a><ul class="menu-list"><li> <a class="is-flex" href="#编码实现"><span class="has-mr-6">4.1</span> <span>编码实现</span></a></li><li> <a class="is-flex" href="#sdk实现"><span class="has-mr-6">4.2</span> <span>sdk实现</span></a></li></ul></li><li> <a class="is-flex" href="#第二篇-微信支付"><span class="has-mr-6">5</span> <span>第二篇: 微信支付</span></a><ul class="menu-list"><li> <a class="is-flex" href="#支付流程"><span class="has-mr-6">5.1</span> <span>支付流程</span></a></li><li> <a class="is-flex" href="#1-统一下单API"><span class="has-mr-6">5.2</span> <span>1. 统一下单API</span></a></li><li> <a class="is-flex" href="#2-微信内H5调起支付"><span class="has-mr-6">5.3</span> <span>2. 微信内H5调起支付</span></a></li><li> <a class="is-flex" href="#3-支付结果通知API"><span class="has-mr-6">5.4</span> <span>3. 支付结果通知API</span></a></li><li> <a class="is-flex" href="#4-申请退款API"><span class="has-mr-6">5.5</span> <span>4. 申请退款API</span></a></li></ul></li><li> <a class="is-flex" href="#第三篇-微信登录功能"><span class="has-mr-6">6</span> <span>第三篇: 微信登录功能</span></a><ul class="menu-list"><li> <a class="is-flex" href="#授权流程"><span class="has-mr-6">6.1</span> <span>授权流程</span></a></li><li> <a class="is-flex" href="#sdk实现-1"><span class="has-mr-6">6.2</span> <span>sdk实现</span></a></li></ul></li><li> <a class="is-flex" href="#第四篇-微信模版消息推送"><span class="has-mr-6">7</span> <span>第四篇: 微信模版消息推送</span></a><ul class="menu-list"><li> <a class="is-flex" href="#1-设置模版"><span class="has-mr-6">7.1</span> <span>1. 设置模版</span></a></li><li> <a class="is-flex" href="#2-发送模版消息"><span class="has-mr-6">7.2</span> <span>2. 发送模版消息</span></a></li></ul></li></ul></div></div></div><div class="column-right-shadow is-hidden-widescreen is-sticky"></div></div></div></div></section><footer class="footer"><div class="container"><div class="level"><div class="level-start has-text-centered-mobile"> <a class="footer-logo is-block has-mb-6" href="/">i蝸居年華_谢谢谢</a><p class="is-size-7"> &copy; 2016—2021 i蝸居年華_谢谢谢&nbsp; 由<a href="http://hexo.io/" target="_blank">Hexo</a> 强力驱动<br> <span id="busuanzi_container_site_uv">您是第<span id="busuanzi_value_site_uv">0</span>个访客</span></p></div><div class="level-end"><div class="field has-addons is-flex-center-mobile has-mt-5-mobile is-flex-wrap is-flex-middle"><p class="control"><a class="button is-white is-large" target="_blank" title="Creative Commons" href="https://creativecommons.org/"><i class="fab fa-creative-commons"></i></a></p><p class="control"><a class="button is-white is-large" target="_blank" title="Attribution 4.0 International" href="https://creativecommons.org/licenses/by/4.0/"><i class="fab fa-creative-commons-by"></i></a></p><p class="control"><a class="button is-white is-large" target="_blank" title="Download on GitHub" href="https://github.com/thank037"><i class="fab fa-github"></i></a></p></div></div></div></div></footer><script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script><script src="https://cdn.jsdelivr.net/npm/moment@2.22.2/min/moment-with-locales.min.js"></script><script>moment.locale("zh-CN")</script><script src="/js/animation.js"></script><script src="https://cdn.jsdelivr.net/npm/lightgallery@1.6.8/dist/js/lightgallery.min.js" defer="defer"></script><script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js" defer="defer"></script><script src="/js/gallery.js" defer="defer"></script><div id="outdated"><h6>Your browser is out-of-date!</h6><p>Update your browser to view this website correctly. <a id="btnUpdateBrowser" href="http://outdatedbrowser.com/">Update my browser now</a></p><p class="last"><a href="#" id="btnCloseUpdateBrowser" title="Close">&times;</a></p></div><script src="https://cdn.jsdelivr.net/npm/outdatedbrowser@1.1.5/outdatedbrowser/outdatedbrowser.min.js" defer="defer"></script><script>document.addEventListener("DOMContentLoaded",function(){outdatedBrowser({bgColor:"#f25648",color:"#ffffff",lowerThan:"flex"})})</script><script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.5/unpacked/MathJax.js?config=TeX-MML-AM_CHTML" defer="defer"></script><script>document.addEventListener("DOMContentLoaded",function(){MathJax.Hub.Config({"HTML-CSS":{matchFontHeight:!1},SVG:{matchFontHeight:!1},CommonHTML:{matchFontHeight:!1},tex2jax:{inlineMath:[["$","$"],["\\(","\\)"]]}})})</script><a id="back-to-top" title="回到顶端" href="javascript:;"><i class="fas fa-chevron-up"></i></a><script src="/js/back-to-top.js" defer="defer"></script><script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.4/dist/clipboard.min.js" defer="defer"></script><script src="/js/clipboard.js" defer="defer"></script><script src="/js/main.js" defer="defer"></script><div class="searchbox ins-search"><div class="searchbox-container ins-search-container"><div class="searchbox-input-wrapper"> <input type="text" class="searchbox-input ins-search-input" placeholder="全文搜索站内文章~"><span class="searchbox-close ins-close ins-selectable"><i class="fa fa-times-circle"></i></span></div><div class="searchbox-result-wrapper ins-section-wrapper"><div class="ins-section-container"></div></div></div></div><script>window.INSIGHT_CONFIG={TRANSLATION:{POSTS:"文章",PAGES:"页面",CATEGORIES:"分类",TAGS:"标签",UNTITLED:"(无标题)"},CONTENT_URL:"/content.json"}</script><script src="/js/insight.js" defer="defer"></script><link rel="stylesheet" href="/css/search.css"><link rel="stylesheet" href="/css/insight.css"><script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({pluginRootPath:"live2dw/",pluginJsPath:"lib/",pluginModelPath:"assets/",tagMode:!1,debug:!1,model:{jsonPath:"/live2dw/assets/hijiki.model.json"},display:{position:"right",width:150,height:300,mobile:{show:!0},react:{opacity:.7}},log:!1})</script></body></html>